/*
 * $Author: isan.wong@gmail.com $
 * $Date: 2011-07-29 01:03:06 +0000 (Fri, 29 Jul 2011) $
 * $Rev: 13 $
 * $URL: http://capitalbio-smd.googlecode.com/svn/src/com/capitalbio/smd/base/Util.java $
 */

package com.capitalbio.smd.base;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.biojava.bio.BioException;
import org.biojava.bio.chromatogram.Chromatogram;
import org.biojava.bio.chromatogram.ChromatogramTools;
import org.biojava.bio.seq.DNATools;
import org.biojava.bio.seq.io.SymbolTokenization;
import org.biojava.bio.symbol.AlphabetManager;
import org.biojava.bio.symbol.AtomicSymbol;
import org.biojava.bio.symbol.FiniteAlphabet;
import org.biojava.bio.symbol.IllegalSymbolException;
import org.biojava.bio.symbol.SimpleSymbolList;
import org.biojava.bio.symbol.Symbol;
import org.biojava.bio.symbol.SymbolList;

/**
 * 工具类
 * 
 * @author Wang Lei
 */
public class Util {

	protected static Log log = LogFactory.getLog(Util.class);

	private static Properties properties;
	static Map<Symbol, Color> symbolColor;
	static Color defaultSymbolColor = new Color(255, 255, 255);
	static Map<Color, String> colorHex;
	static Locale locale;
	static ResourceBundle resourceBundle;
	
	static {
		properties = new Properties();
		try {
			properties.load(new FileReader(new File(Util.getResourceURL(
					"smd.properties").toURI())));
		} catch (Exception e) {
			log.error(e);
		}
		
		symbolColor = new HashMap<Symbol, Color>();
		symbolColor.put(getGapSymbol(), new Color(255, 255, 255));
		symbolColor.put(DNATools.a(), new Color(0, 255, 0));
		symbolColor.put(DNATools.c(), new Color(0, 0, 255));
		symbolColor.put(DNATools.g(), new Color(0, 0, 0));
		symbolColor.put(DNATools.t(), new Color(255, 0, 0));
		symbolColor.put(DNATools.m(), new Color(0, 255, 255));
		symbolColor.put(DNATools.r(), new Color(0, 200, 0));
		symbolColor.put(DNATools.w(), new Color(255, 255, 0));
		symbolColor.put(DNATools.s(), new Color(0, 0, 200));
		symbolColor.put(DNATools.y(), new Color(255, 0, 255));
		symbolColor.put(DNATools.k(), new Color(200, 0, 0));
		
		colorHex = new HashMap<Color, String>();
		colorHex.put(symbolColor.get(getGapSymbol()), "#ffffff");
		colorHex.put(symbolColor.get(DNATools.a()), "#00ff00");
		colorHex.put(symbolColor.get(DNATools.c()), "#0000ff");
		colorHex.put(symbolColor.get(DNATools.g()), "#000000");
		colorHex.put(symbolColor.get(DNATools.t()), "#ff0000");
		colorHex.put(symbolColor.get(DNATools.m()), "#00ffff");
		colorHex.put(symbolColor.get(DNATools.r()), "00c800");
		colorHex.put(symbolColor.get(DNATools.w()), "#ffff00");
		colorHex.put(symbolColor.get(DNATools.s()), "0000c8");
		colorHex.put(symbolColor.get(DNATools.y()), "#ff00ff");
		colorHex.put(symbolColor.get(DNATools.k()), "c80000");
		
		// i18n
		locale = new Locale(
			properties.getProperty("smd.program.languange"),
			properties.getProperty("smd.program.country")
		);
		resourceBundle = ResourceBundle.getBundle(
			"resource.i18n.message",
			locale
		);
	}
	
	/**
	 * i18n
	 */
	public static String getText(String key) {
		return resourceBundle.getString(key);
	}

	/**
	 * 生成callbox数据
	 * 
	 * @param chromat
	 * @return callbox 数据, int[sequenceLength][2], 二维数组
	 */
	public static int[][] generateCallBoxes(Chromatogram chromat) {
		int[][] callboxes = null;
		if (chromat.getSequenceLength() < 2) {
			callboxes = new int[1][2];
			callboxes[0] = new int[] { 0, chromat.getTraceLength() - 1 };
		} else {
			int[] bcOffsets = ChromatogramTools.getTraceOffsetArray(chromat);
			if (callboxes == null || callboxes.length != bcOffsets.length) {
				callboxes = new int[bcOffsets.length][2];
			}
			int leftIdx = 0;
			int rightIdx = bcOffsets[0]
					+ (int) Math
							.floor(((double) (bcOffsets[1] - bcOffsets[0])) / 2)
					+ 1;
			int left = leftIdx;
			int right = rightIdx;
			callboxes[0] = new int[] { left, right };

			int i = 1;
			while (i < bcOffsets.length - 1) {
				leftIdx = rightIdx;
				rightIdx = bcOffsets[i]
						+ (int) Math
								.floor(((double) (bcOffsets[i + 1] - bcOffsets[i])) / 2)
						+ 1;
				left = right;
				right = rightIdx;
				callboxes[i] = new int[] { left, right };
				i++;
			}
			left = right;
			right = chromat.getTraceLength() - 1;
			callboxes[i] = new int[] { left, right };
		}
		return callboxes;
	}

	/**
	 * 读取混合碱基 percent=0, hPercent=0.3
	 * 
	 * @param chromat
	 * @return List<Symbol>
	 * @see getMixedBaseSeq
	 */
	public static SymbolList getMixedBaseSeq(Chromatogram chromat) {
		return getMixedBaseSeq(chromat, 0.0F, 0.3F);
	}

	/**
	 * 读取混合碱基
	 * 
	 * @param chromat
	 * @param percent
	 *            0~1 0代表在offset处取值, 1代表在整个callbox内取值
	 * @param hPercent
	 *            高度比例阈值, 超过此阈值算作次级碱基
	 * @return List<Symbol>
	 */
	public static SymbolList getMixedBaseSeq(Chromatogram chromat,
			float percent, float hPercent) {
		List<Symbol> mixedBaseSeq = new ArrayList<Symbol>();
		try {
			@SuppressWarnings("unchecked")
			List<Symbol> dna = ChromatogramTools.getDNASequence(chromat)
					.toList();
			int[] offsets = ChromatogramTools.getTraceOffsetArray(chromat);
			int[][] callboxes = Util.generateCallBoxes(chromat);

			// 单个碱基类型序列
			Map<AtomicSymbol, int[]> symbolTrace = new HashMap<AtomicSymbol, int[]>();
			symbolTrace.put(DNATools.a(), chromat.getTrace(DNATools.a()));
			symbolTrace.put(DNATools.c(), chromat.getTrace(DNATools.c()));
			symbolTrace.put(DNATools.g(), chromat.getTrace(DNATools.g()));
			symbolTrace.put(DNATools.t(), chromat.getTrace(DNATools.t()));

			// 检查混合碱基
			int sequenceLength = chromat.getSequenceLength();
			for (int i = 0; i < sequenceLength; i++) {
				int left = (int) Math.floor((double) callboxes[i][0]
						+ (offsets[i] - callboxes[i][0]) * (1 - percent));
				int right = (int) Math.floor((double) offsets[i]
						+ (callboxes[i][0] - offsets[i]) * percent);

				AtomicSymbol baseCall = (AtomicSymbol) dna.get(i);
				int baseCallIntensity = symbolTrace.get(baseCall)[offsets[i]];

				int maxIntensity = 0;
				AtomicSymbol maxIntensityBase = null;
				for (int j = left; j <= right; j++) {
					for (AtomicSymbol symbol : symbolTrace.keySet()) {
						if (!symbol.equals(baseCall)) {
							int intensity = symbolTrace.get(symbol)[j];
							if (intensity > maxIntensity) {
								maxIntensity = intensity;
								maxIntensityBase = symbol;
							}
						}
					}
				}

				float intensityPersent = (float) maxIntensity
						/ (float) baseCallIntensity;

				// System.out.println(i + " " + baseCall.getName() + " " +
				// maxIntensity + " " + baseCallIntensity + " " +
				// intensityPersent);

				if (maxIntensityBase != null && intensityPersent >= hPercent) {
					Set<AtomicSymbol> mixedBaseSet = new HashSet<AtomicSymbol>();
					mixedBaseSet.add(baseCall);
					mixedBaseSet.add(maxIntensityBase);
					Symbol mixedBase = DNATools.getDNA().getAmbiguity(
							mixedBaseSet);

					mixedBaseSeq.add(mixedBase);
				} else {
					mixedBaseSeq.add(baseCall);
				}
			}
		} catch (IllegalSymbolException e) {
			log.error(e);
		}
		
		SymbolList symbolList = null;
		try {
			symbolList = new SimpleSymbolList(DNATools.getDNA(), mixedBaseSeq);
		} catch (IllegalSymbolException e) {
			log.error(e);
		}
		
		return symbolList;
	}

	/**
	 * 获取碱基颜色
	 * @param symbol
	 * @return 颜色
	 */
	public static Color getSymbolColor(Symbol symbol) {
		if (symbolColor.containsKey(symbol)) {
			return symbolColor.get(symbol);
		}
		else {
			return defaultSymbolColor;
		}
	}
	
	/**
	 * 默认颜色
	 * @return 默认颜色
	 */
	public static Color getDefaultSymbolColor() {
		return defaultSymbolColor;
	}
	
	/**
	 * 将颜色转换为网页 16 进制的格式
	 * @param color
	 * @return such as #F0F0F0
	 */
	public static String colorHex(Color color) {
		if (colorHex.containsKey(color)) {
			return colorHex.get(color);
		}
		else {
			int red = color.getRed();
			int green = color.getGreen();
			int blue = color.getBlue();
			String r = Integer.toHexString(color.getRed());
			if (red < 17) {
				r = "0" + r;
			}
			String g = Integer.toHexString(color.getGreen());
			if (green < 17) {
				g = "0" + g;
			}
			String b = Integer.toHexString(color.getBlue());
			if (blue < 17) {
				b = "0" + b;
			}
	
			return "#" + r + g + b;
		}
	}

	/**
	 * 从 seq1 中减去 seq2 seq1 的长度需和 seq 的长度相等, 否则抛出 IllegalArgumentException
	 * 
	 * @param seq1
	 * @param seq2
	 * @return seq3, seq4
	 * @throws IllegalArgumentException
	 */
	public static List<SymbolList> subtractSeq(SymbolList seq1, SymbolList seq2)
			throws IllegalArgumentException {
		if (seq1.length() != seq2.length()) {
			throw new IllegalArgumentException(
					"Two sequences have different length, can't make substraction.");
		}

		@SuppressWarnings("unchecked")
		Iterator<Symbol> seq1Iterator = seq1.iterator();
		@SuppressWarnings("unchecked")
		Iterator<Symbol> seq2Iterator = seq2.iterator();

		List<Symbol> symbols3 = new ArrayList<Symbol>();
		List<Symbol> symbols4 = new ArrayList<Symbol>();
		while (seq1Iterator.hasNext()) {
			Symbol base1 = seq1Iterator.next();
			Symbol base2 = seq2Iterator.next();

			Symbol base3 = null;
			Symbol base4 = null;

			// 跳过gap
			if (base1 == getGapSymbol()) {
				continue;
			}

			// 可以相减的情况
			Set<Symbol> bases3 = new HashSet<Symbol>();
			FiniteAlphabet alphabet = (FiniteAlphabet) base1.getMatches();
			if (alphabet.size() > 1) {
				for (Iterator<Symbol> iterator = alphabet.iterator(); iterator
						.hasNext();) {
					Symbol symbol = iterator.next();
					if (symbol != base2) {
						bases3.add(symbol);
					}
				}
				if (bases3.size() > 0) {
					try {
						base3 = DNATools.getDNA().getAmbiguity(bases3);
						base4 = base2;
					} catch (IllegalSymbolException e) {
						log.error(e);
					}
				}
			}

			// 如果相减没有结果
			if (base3 == null || base4 == null) {
				base3 = base1;
				base4 = base1;
			}

			symbols3.add(base3);
			symbols4.add(base4);
		}

		SymbolList seq3 = getSequence(symbols3, "strand_1");
		SymbolList seq4 = getSequence(symbols4, "strand_2");

		List<SymbolList> sequences = new ArrayList<SymbolList>();
		sequences.add(seq3);
		sequences.add(seq4);

		return sequences;
	}

	public static SymbolList getSequence(List<Symbol> symbols, String name) {
		SymbolList symbolList = null;
		try {
			symbolList = new SimpleSymbolList(DNATools.getDNA(), symbols);
		} catch (IllegalSymbolException e) {
			log.error(e);
		}
		return symbolList;
	}

	public static URL getResourceURL(String resource) {
		return Util.class.getResource("/resource/" + resource);
	}

	public static Symbol getGapSymbol() {
		return ((FiniteAlphabet) AlphabetManager.alphabetForName("DNA"))
				.getGapSymbol();
	}

	public static SymbolTokenization getDNATokenization() {
		FiniteAlphabet dna = (FiniteAlphabet) AlphabetManager
				.alphabetForName("DNA");
		SymbolTokenization dnaTokens = null;
		try {
			dnaTokens = dna.getTokenization("token");
		} catch (BioException e) {
			log.error(e);
		}
		return dnaTokens;
	}
	
	public static String getSymbolToken(Symbol symbol) {
		String token = null;
		try {
			token = Util.getDNATokenization().tokenizeSymbol(symbol).toUpperCase();
		} catch (IllegalSymbolException e) {
			log.error(e);
		}
		
		return token;
	}
	
	/**
	 * 如果 symbol1 包含/等于 symbol2 就返回 true, 否则返回 false (任意一个为 gap, 返回 false)
	 * @param symbol1
	 * @param symbol2
	 * @return symbol1 与 symbol2 的是否相等
	 */
	public static boolean symbolsIdentity(Symbol symbol1, Symbol symbol2) {

		// 只要有一个是gap就返回false
		if (symbol1 == getGapSymbol() || symbol2 == getGapSymbol()) {
			return false;
		}
		else if (symbol1.getMatches().contains(symbol2)) {
				return true;
		}
		else {
			return false;
		}
	}

	public static String formatSequence1(String seqString, int lineLength) {
		List<String> seqStrings = formatSequence(seqString, lineLength);
		return joinString(seqStrings.toArray(), "\n");
	}
	
	public static String formatSequence2(String seqString, int lineLength, int sectionLength) {
		List<String> seqStrings = formatSequence(seqString, lineLength);
		List<String> formattedSeqStrings = new ArrayList<String>();
		for (String seqStr : seqStrings) {
			List<String> sectionStrings = formatSequence(seqStr, sectionLength);
			formattedSeqStrings.add(joinString(sectionStrings.toArray(), " "));
		}
		return joinString(formattedSeqStrings.toArray(), "\n");
	}
	
	private static List<String> formatSequence(String seqString, int sectionLength) {
		int length = seqString.length();
		int times = length / sectionLength;
		int lastLineLength = length % sectionLength;

		List<String> seqStrings = new ArrayList<String>();
		for (int i = 0; i <= times; i++) {
			
			if (lastLineLength == 0 && i == times) {
				break;
			}
			
			String subSequence = null;
			if (i < times) {
				subSequence = seqString.substring(i * sectionLength,
						(i + 1) * sectionLength);
			} else {
				subSequence = seqString.substring(i * sectionLength,
						i * sectionLength + lastLineLength);
			}
			seqStrings.add(subSequence);
		}
		return seqStrings;
	}
	
	public static String formatSequencesIdentity(SymbolList sequence1,
			SymbolList sequence2) {
		// 检查长度
		int length = sequence1.length();
		if (sequence2.length() != length) {
			throw new IllegalArgumentException(
					"Sequences have different length.");
		}

		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 1; i <= length; i++) {
			if (symbolsIdentity(sequence1.symbolAt(i), sequence2.symbolAt(i))) {
				stringBuilder.append("*");
			} else {
				stringBuilder.append(" ");
			}
		}

		return stringBuilder.toString();
	}

	public static String formatAlignment(int lineLength, SymbolList... sequences) {
		// 检查长度
		int length = sequences[0].length();
		for (int i = 1; i < sequences.length; i++) {
			if (sequences[i].length() != length) {
				throw new IllegalArgumentException(
						"Sequences have different length.");
			}
		}

		StringBuilder stringBuilder = new StringBuilder();

		int times = length / lineLength;
		int lastLineLength = length % lineLength;

		for (int i = 0; i <= times; i++) {
			SymbolList subSequence0 = null;
			for (int j = 0; j < sequences.length; j++) {
				SymbolList subSequence = null;
				if (i < times) {
					subSequence = sequences[j].subList(i * lineLength + 1,
							(i + 1) * lineLength);
				} else if (lastLineLength > 0) {
					subSequence = sequences[j].subList(i * lineLength + 1,
							i * lineLength + lastLineLength);
				}
				else {
					continue;
				}

				stringBuilder.append(subSequence.seqString().toUpperCase());
				stringBuilder.append("\n");
				
				if (j == 0) {
					subSequence0 = subSequence;
				} else {
					stringBuilder.append(formatSequencesIdentity(subSequence0,
							subSequence));
					stringBuilder.append("\n");
				}

			}
			stringBuilder.append("\n");
		}
		return stringBuilder.toString();
	}

	public static Properties getProperties() {
		return properties;
	}
	
	/**
	 * get string property
	 * @param key
	 * @return value
	 */
	public static String getProperty(String key) {
		return properties.getProperty(key);
	}
	
	/**
	 * get color property
	 * @param key
	 * @return color
	 */
	public static Color getColorProperty(String key) {
		String property = properties.getProperty(key);
		String[] rgb = property.split(",");
		return new Color(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2]));
	}
	
	/**
	 * get matrix file / name
	 * @param key
	 * @return string value, matrix name or matrix file path
	 */
	public static String getMatrixProperty(String key) {
		String value = Util.getProperties().getProperty(key);
		String upperValue = value.toUpperCase();
		if (upperValue.equals("IUB") || upperValue.equals("CLUSTAL")) {
			return upperValue;
		}
		else {
			try {
				File matrixFile = new File(Util.getResourceURL("matrices/" + value + ".matrix").toURI());
				return matrixFile.getAbsolutePath();
			} catch (URISyntaxException e) {
				log.error(e);
				return null;
			}
		}
	}
	
	/**
	 * get float property
	 */
	public static Float getFloatProperty(String key) {
		return Float.parseFloat(properties
				.getProperty(key));
	}
	
	/**
	 * get integer property
	 */
	public static Integer getIntProperty(String key) {
		return Integer.parseInt(properties
				.getProperty(key));
	}
	
	/**
	 * get boolean property
	 */
	public static Boolean getBooleanProperty(String key) {
		return Boolean.parseBoolean(properties
				.getProperty(key));
	}
	
	/**
	 * region values, a list of int[2] (eg. start/stop)
	 * @param regionsStr like 10-11,15,17-19 etc.
	 * @return List<int[]>
	 */
	public static List<int[]> processRegionProperty(String regionsStr) {
		List<int[]> regions = new ArrayList<int[]>();
		String[] regionStrs = regionsStr.split(",");
		for (int i = 0; i < regionStrs.length; i++) {
			String[] regionStr = regionStrs[i].split("-");
			if (regionStr.length == 1) {
				regions.add(new int[] {Integer.parseInt(regionStr[0]), Integer.parseInt(regionStr[0]) });
			}
			else if (regionStr.length == 2) {
				regions.add(new int[] {Integer.parseInt(regionStr[0]), Integer.parseInt(regionStr[1]) });
			}
		}

		return regions;
	}

	public static String getSymbolLabel(Symbol symbol) {
		StringBuilder labelBuilder = new StringBuilder();
		try {
			String name = Util.getDNATokenization().tokenizeSymbol(symbol)
					.toUpperCase();
			labelBuilder.append(name);
		} catch (IllegalSymbolException e) {
			log.error(e);
		}

		/*
		FiniteAlphabet alphabet = (FiniteAlphabet) symbol.getMatches();
		if (alphabet.size() > 1) {
			labelBuilder.append(" (");
			for (Iterator<Symbol> iterator = alphabet.iterator(); iterator
					.hasNext();) {
				Symbol match = iterator.next();
				try {
					String matchName = Util.getDNATokenization()
							.tokenizeSymbol(match).toUpperCase();
					labelBuilder.append(matchName);
				} catch (IllegalSymbolException e) {
					log.error(e);
				}
			}
			labelBuilder.append(")");
		}
		*/
		
		return labelBuilder.toString();
	}

	/**
	 * 读取文本文件
	 * 
	 * @param file
	 * @return 整个文件的内容
	 */
	public static String fileToString(File file) {

		String fileBody = "";
		boolean endOfFile = false;

		try {

			FileReader fileClustalW = new FileReader(file);
			BufferedReader fileBuffer = new BufferedReader(fileClustalW);

			while (!endOfFile) {
				String fileLine = fileBuffer.readLine();

				if (fileLine == null) {
					endOfFile = true;
				} else {
					fileBody = fileBody.concat(fileLine);
				}
			}
			fileBuffer.close();

		} catch (IOException e) {
			log.error(e);
		}

		return fileBody;
	}

	public static String joinString(Object[] s, String delimiter) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < s.length; i++) {
			buffer.append((String) s[i]);
			if (i < s.length - 1) {
				buffer.append(delimiter);
			}
		}
		return buffer.toString();
	}
	
	/**
	 * 分析序列中的 gap
	 * 
	 * @param sequence
	 * @return List<int[2]> 返回gap的位置及长度 (注意, 位置从 1 开始)
	 */
	public static List<int[]> findGaps(SymbolList symbolList) {
		String gapChar = null;
		try {
			gapChar = Util.getDNATokenization().tokenizeSymbol(Util.getGapSymbol()).toUpperCase();
		} catch (IllegalSymbolException e) {
			log.error(e);
		}
		Pattern pattern = Pattern.compile(gapChar + "+");
		Matcher matcher = pattern.matcher(symbolList.seqString());
		List<int[]> gaps = new ArrayList<int[]>();
		while (matcher.find()) {
			int[] gap = new int[2];
			gap[0] = matcher.start() + 1;
			gap[1] = matcher.end();
			gaps.add(gap);
		}
		return gaps;
	}

	/**
	 * get start gap length, 0 means no gap at start of course
	 * @param symbolList
	 * @return gap length
	 */
	public static int getStartGapLength(SymbolList symbolList) {
		int startGapLength = 0;
		for (int i = 1; i <= symbolList.length(); i++) {
			if (symbolList.symbolAt(i) == getGapSymbol()) {
				startGapLength ++;
			}
			else {
				break;
			}
		}
		
		return startGapLength;
	}
	
	/**
	 * get maximum start gap length in the list of SymbolList
	 * @param symbolLists
	 * @return maximum gap length
	 */
	public static int getMaxStartGapLength(SymbolList... symbolLists) {
		int maxStartGapLength = 0;
		for (SymbolList symbolList : symbolLists) {
			int startGapLength = getStartGapLength(symbolList);
			if (startGapLength > maxStartGapLength) {
				maxStartGapLength = startGapLength;
			}
		}
		return maxStartGapLength;
	}
	
	/**
	 * get stop gap length, 0 means no gap at stop of course
	 * @param symbolList
	 * @return gap length
	 */
	public static int getStopGapLength(SymbolList symbolList) {
		int stopGapLength = 0;
		for (int i = symbolList.length(); i > 0; i--) {
			if (symbolList.symbolAt(i) == getGapSymbol()) {
				stopGapLength ++;
			}
			else {
				break;
			}
		}
		
		return stopGapLength;
	}
	
	/**
	 * get maximum stop gap length in the list of SymbolList
	 * @param symbolLists
	 * @return maximum gap length
	 */
	public static int getMaxStopGapLength(SymbolList... symbolLists) {
		int maxStopGapLength = 0;
		for (SymbolList symbolList : symbolLists) {
			int stopGapLength = getStopGapLength(symbolList);
			if (stopGapLength > maxStopGapLength) {
				maxStopGapLength = stopGapLength;
			}
		}
		return maxStopGapLength;
	}
	
	/**
	 * trim sequence
	 * @param symbolList
	 * @param startTrimLength
	 * @parma stopTrimLength
	 */
	public static SymbolList trimSequence(SymbolList symbolList, int startTrimLength, int stopTrimLength) {
		return symbolList.subList(startTrimLength + 1, symbolList.length() - stopTrimLength);
	}

}
